package org.diy.traffic.config;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.logging.Level;

import org.diy.traffic.util.TLog;


/**
* @author Shanks Krishnan, Dan Greenblatt
*DIYConfig is the class that encapsulates the diycity.config file. 
*This class has helper methods to retrieve all the city information in the config file as an ArrayList of DIYCity objects. 
*/


public class TrafficUKConfig {
    
    
	 /**
     * Sole constructor.  (For invocation by instantiating class 
     * typically implicit. Can be overridden)
     */

	private HashMap<String, DIYUKCity> registeredCities = new HashMap<String, DIYUKCity>();
	private String defaultConfigurationFile = null;
	
    public TrafficUKConfig(){}
 
    public void registerCities(ArrayList<DIYUKCity> cities) {
    	Iterator<DIYUKCity> i = cities.iterator();
    	while (i.hasNext()) {
    		registerCity(i.next());
    	}		
    }
    
    public void registerCity(DIYUKCity c) {
    	registeredCities.put(c.getEncodedAddress(), c);
    }
    
    public void unregisterCity(String encodedAddress) {
    	registeredCities.remove(encodedAddress);
    }
    
    public void unregisterCity(String cityName, String stateCode) {
    	registeredCities.remove(
    			encodeAddress(new String[]{cityName, stateCode})
    			);
    	
    }
    
    public void clearRegisteredCities() {
    	registeredCities.clear();
    }
       
    public DIYUKCity isCityRegistered(String cityName) {
    	return registeredCities.get(
    			encodeAddress(new String[]{cityName})
    			);
    }
    
    public void setDefaultConfigurationFile(String filePath) {
    	this.defaultConfigurationFile = filePath;
    }
    
    public String getDefaultConfigurationFile() {
    	return this.defaultConfigurationFile;
    }
    
    public ArrayList<DIYUKCity> configureFromRemoteFile(String remoteConfigURI) throws TrafficUKConfigException {
    	File f = this.getRemoteConfigFile(remoteConfigURI);
    	return configureFromLocalFile(f.getAbsolutePath());
    }
	
    public ArrayList<DIYUKCity> determineNewCities(ArrayList<DIYUKCity> newCities) {
    	
    	ArrayList<DIYUKCity> delta = new ArrayList<DIYUKCity>();
    	
    	Iterator<DIYUKCity> i = newCities.iterator();
    	while (i.hasNext()) {
    		DIYUKCity c = i.next();
    		if (!registeredCities.containsKey(c.getEncodedAddress())) {
    			delta.add(c);
    		}
    	}
    	
    	return delta;
    }
    
    /**
     * This method reads the Configuration File and encapsulates the information in the cityList ArrayList. This method is called 
     * by the constructor and should not be overridden unless the Configuration format changes
     *
     */
    public ArrayList<DIYUKCity> configureFromLocalFile(String filePath) throws TrafficUKConfigException {
    	ArrayList <DIYUKCity> cityList = new ArrayList <DIYUKCity>();
    	
		//InputStreamReader configFile=new InputStreamReader(this.getClass().getResourceAsStream(configFileName));
    	File f = new File(filePath);
    	
    	InputStreamReader configFileStream = null;
    	BufferedReader bufferedReader = null;
    	try {
    		configFileStream = new InputStreamReader(new FileInputStream(f));
    	} catch (FileNotFoundException fnfe) {
    		fnfe.printStackTrace();
    		TLog.log(Level.SEVERE, "Can't open config file " + f.getAbsolutePath());
    		throw new TrafficUKConfigException(fnfe.getMessage());
    	}
    	
    	if (configFileStream != null) 
    		bufferedReader = new BufferedReader(configFileStream);
    	else {
    		TLog.log(Level.SEVERE, "Can't open stream to config file");
    		throw new TrafficUKConfigException("Unable to open config file " + f.getAbsolutePath());
    	}
    	
        String line = null;
        
        while (true) {
        	
        	try {
				line = bufferedReader.readLine();
			} catch (IOException e) {
				e.printStackTrace();
				throw new TrafficUKConfigException("Error occurred while reading line from config file");
			}
			
			//System.out.println("LINE IS " + line);
			
			if (line == null) break;
        	
			line = line.trim();
			
			if (line.isEmpty()) {
        		// just a blank line here
        		continue;
        	} else if ( line.substring(0, 2).compareTo("//") == 0) {
        		// if this line is a comment
        		continue;
        	}
        	
        	if (line.contains("<br>")) {
        		line = line.substring(0, line.indexOf("<br>"));
        	}
        	
        	String[] elements = line.split(",");
        	if (elements.length != 3) {
        		TLog.log(Level.WARNING, String.format("line '%s' should contain three elements, only contains %s. Ignoring.", line, elements.length));
        		continue;
        	}
        	
        	String city_name=elements[0].trim();
    		String twitter_username = elements[1].trim();	
    		String twitter_password = elements[2].trim();
    		
        	
    		TLog.log(Level.CONFIG, 
    				String.format("Found city info in the config file: %s, %s, %s", city_name,  twitter_username, twitter_password)
    		);
            		
    		cityList.add(
    				new DIYUKCity(city_name.toLowerCase(), twitter_username, twitter_password)
    				);                	
        }
	
        return cityList;
    }
            
  
    
    
   
    
    
    private File getRemoteConfigFile(String remoteFileURI) throws TrafficUKConfigException {
    	
    	
		final URLConnection connection;
		final InputStream inStream;
		try {
			connection = new URL(remoteFileURI).openConnection();
		} catch (IOException ex) {
			throw new TrafficUKConfigException("Unable to fetch remote config file");
		}
		
		/**
		// pretend to be IE6 on Windows XP SP2
		// http://en.wikipedia.org/wiki/User_agent#Internet_Explorer
		connection
				.setRequestProperty("User-Agent",
						"Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 2.0.50727)");
		connection.setDoInput(true);
		**/
		
		
		// ten second time-out
		connection.setReadTimeout(10 * 1000); 
			
		// Open a connection
		try {
			inStream = connection.getInputStream();
		} catch (FileNotFoundException e) { // This happens with 404s only
			throw new TrafficUKConfigException("Can't find remote config file (404 Error) on '" + remoteFileURI + "'");
		} catch (IOException e) { // All other problems
			TLog.log(Level.WARNING, "Unknown error while connecting to remote config file host");
			throw new TrafficUKConfigException(e.getMessage());
		}

		InputStreamReader reader = new InputStreamReader(inStream);
		BufferedReader bReader = new BufferedReader(reader);
		
		StringBuilder output = new StringBuilder();
		
		try {
			while (true) {
		
			int c = bReader.read();
			if (c == -1)
				break;
			output.append((char) c);
			}
		} catch (IOException ioe) {
			TLog.log(Level.WARNING, "Error downloading contents of remote config");
			throw new TrafficUKConfigException(ioe.getMessage());
		}
		
		String remote_file_contents = output.toString();
		File temp_file;
		try {
			temp_file = File.createTempFile("remoteconfig", "temp");
		} catch (IOException e) {
			TLog.log(Level.SEVERE, "Can't create a temp file");	
			throw new TrafficUKConfigException(e.getMessage());
		}
		
		BufferedWriter config_writer=null;
    	try {

    		String confFileName=temp_file.getAbsolutePath();
    		config_writer=new BufferedWriter(new FileWriter(confFileName));
			config_writer.write(remote_file_contents);
			config_writer.flush();			
		} catch (IOException e) {
			TLog.log(Level.WARNING, "Error writing to temp file " + temp_file.getAbsolutePath());	
			throw new TrafficUKConfigException(e.getMessage());
		}
		
		TLog.log(Level.INFO, "GOT REMOTE FILE CONTENTS!:\n " + remote_file_contents);
		return temp_file;
    }
       
    public String encodeAddress(String[] address) {
		String address_str = "";
		for (int i=0; i<address.length; i++){
			address_str += address[i].replace(" ", "-");
			if (i < address.length-1) address_str += "_";
		}
		return address_str;
	}
    
    public class TrafficUKConfigException extends Exception {

		public TrafficUKConfigException() {
			super();
		}

		public TrafficUKConfigException(String arg0) {
			super(arg0);
		}
    	
    }

	public ArrayList<DIYUKCity> configureFromDefaultFile() throws TrafficUKConfigException{
		if (this.defaultConfigurationFile != null) {
			return this.configureFromLocalFile(this.defaultConfigurationFile);
		} else
			return null;
	
	}
}
